package de.tum.in.bluetooth;

import java.io.IOException;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

public class BluetoothClientP2P extends BluetoothClient 
{
	String serviceName;
	WaitingForConnection waitingForConnection;
	
	public BluetoothClientP2P(Handler messageHandler, String serviceName) 
	{
		super(messageHandler);
		this.serviceName = serviceName;
	}
	
	public synchronized void connectToDevice(BluetoothDevice tDevice)
	{
		Log.i("BluetoothClient", "Wants to connect to device "+tDevice.getName());
		this.device = tDevice;
		this.notify();
	}
	
	public synchronized void connectedToSocket(BluetoothSocket socket)
	{	
		Log.i("BluetoothClient", "Connected to socket "+socket.getRemoteDevice().getName());
		this.socket = socket;
		this.device = socket.getRemoteDevice();
		isConnected=true;
		
		if(waitingForConnection!=null)
		{
			waitingForConnection.cancel();
		    waitingForConnection = null;
		}
		
		this.notify();
		
		if(asyncHandler!=null)
			asyncHandler.obtainMessage(REQUESTED,"requested by "+device.getName()).sendToTarget();
	}
	
	private void connect()
	{
		try 
		{	
			Log.i("BluetoothClient", "Try to connect to "+device.getName());
			
			if(asyncHandler!=null)
				asyncHandler.obtainMessage(CONNECTING,"connecting to "+device.getName()).sendToTarget();
			
			/*
			Method m = device.getClass().getMethod("createRfcommSocket",
		            new Class[] { int.class });
		        socket = (BluetoothSocket)m.invoke(device, Integer.valueOf(1));
			*/
			
			socket = device.createRfcommSocketToServiceRecord(uid);
			
			if(waitingForConnection!=null)
			{
				waitingForConnection.cancel();
			    waitingForConnection = null;
			}
			
			socket.connect();
			isConnected=true;
			        
			Log.i("BluetoothClient", "Connected to "+device.getName());
			
			if(asyncHandler!=null)
				asyncHandler.obtainMessage(CONNECTED,"connected to "+device.getName()).sendToTarget();
			
		} catch (Exception e) {
			Log.i("BluetoothClient", "! Connecting to "+device.getName()+" failed ("+e.getMessage()+")");
			e.printStackTrace();
			
			if(asyncHandler!=null)
				asyncHandler.obtainMessage(EXCEPTION,"Client "+e.getMessage()).sendToTarget();
			
			try {
				socket.close();
			} catch (Exception e2) {
				e.printStackTrace();
			}
			
			socket=null;
			device=null;
			
			isConnected=false;
		}
	}
	
	public void run() 
	{
		Log.i("BluetoothClient", "Client started ");

		while(active)
		{
			while(active && socket == null)
			{
				Log.i("BluetoothClient", "Worker Loop");
				//Try to connect
				if(device != null)
				{
					//Got an connect wish from outside
					this.connect();
				}else{
					//Open serversocket
					if(waitingForConnection == null)
					{
						waitingForConnection = new WaitingForConnection(this);
						waitingForConnection.start();
					}
					
					//Wait for connection wish
					synchronized(this)
					{
						try 
						{
							Log.i("BluetoothClient", "Wait until action");
							this.wait();
						} catch (InterruptedException e) {
							Log.i("BluetoothClient", "Unable to wait "+e.getMessage());
						}
						Log.i("BluetoothClient", "Notified after wait ");
					}
				}
			}
			
			while(active && socket!=null)
			{
				Log.i("BluetoothClient", "Message Loop");
				this.receiveObject();
			}
		}
		
		Log.i("BluetoothClient","Communication Thread ended");
	}
	
	@Override
	public synchronized void disconnect()
	{
		super.disconnect();
		
		if(asyncHandler!=null)
			asyncHandler.obtainMessage(DISCONNECTED,"Client disconnected").sendToTarget();
		
	}
	
	@Override
	public synchronized void cancel()
	{
		super.cancel();
		
		if(waitingForConnection!=null)
			waitingForConnection.cancel();
		
		waitingForConnection=null;
		
		this.notify();
	}
	
	private class WaitingForConnection extends Thread {

		BluetoothClientP2P client;
		BluetoothServerSocket serverSocket;
		
        public WaitingForConnection(BluetoothClientP2P client) 
        {
        	super();
        	this.client=client;
        }

        public void run()
        {
        	try 
   		 	{
        		Looper.myLooper();
				Looper.prepare();
        		
	   			BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
	   			
	   			serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(serviceName,uid);
	   			 
	   			Log.i("BluetoothClientWorker", "Waiting for Connection");
	   			
	   			//Connect to client coming
	   			BluetoothSocket socket = serverSocket.accept();
	   			client.connectedToSocket(socket);
	   			
	   	     } catch (IOException e) 
	   	     {
	   	    	 if(!e.getMessage().equals("Operation Canceled"))
	   	    	 {
	   	    		 Log.i("BluetoothClientWorker", "!Waiting for Connection failed:"+e.getMessage());
	   	    		 client.cancel();
	   	    	 }
	   	    }
	   	   
	   	  //Cancel if not canceled from outside
	   	  this.cancel();
	   	  
	   	  Log.i("BluetoothClientWorker","Server Thread ended");
        }
        
        public synchronized void cancel()
        {	
	   	    try 
	   	    {
	   	    	if(serverSocket!=null)
	   	    	{
	   	    		serverSocket.close();
	   	    		serverSocket=null;
	   	    		Log.i("BluetoothClientWorker", "ServerSocket closed");
	   	    	}
			} catch (Exception e) {
				Log.i("BluetoothClientWorker", "Server Close error "+e.getMessage());
			}
 			
        }
    }

}
